package com.suli.account.component;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.suli.account.entity.Message;
import com.suli.account.entity.User;
import com.suli.account.enums.Status;
import com.suli.account.service.UserService;
import com.suli.account.util.MessageUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @ ServerEndpoint 这个注解有什么作用？
 *
 * 这个注解用于标识作用在类上，它的主要功能是把当前类标识成一个WebSocket的服务端
 * 注解的值用户客户端连接访问的URL地址
 */
@Slf4j
@Component
@ServerEndpoint("/message/{name}")
public class WebSocketServer {

    private static UserService userService;

    @Autowired
    public void setUserService (UserService userService){
        WebSocketServer.userService= userService;
    }

    /**
     * 与某个客户端的连接对话，需要通过它来给指定的客户端发送消息，每个用户都要有且不一样，因此不能声明为static
     */
    private Session session;

    /**
     * 标识当前连接客户端的用户名
     */
    private String name="";

    private static String realAnnounce="";
    /**
     * 用于存所有的连接服务的客户端，这个对象存储是安全的
     */
    private static final ConcurrentHashMap<String, WebSocketServer> onlineUsers =
            new ConcurrentHashMap<>();

    /**
     * 连接建立是被调用
     * @param session 当前建立的会话
     * @param name url传参
     */
    @OnOpen
    public void OnOpen(Session session, @PathParam("name") String name) {
//将局部session赋值给全局的session
        this.session = session;
        //将前端请求路径的name取出
        this.name = name;
        //将登录的用户名进行记录，this是当前的WebSocketServer对象
        onlineUsers.put(name, this);
        //后台日志输出
        log.info(name+"：连接成功，当前连接人数为：{}", onlineUsers.size());
        //将当前登录的(所有)用户信息进行系统广播
        //1.将要发送的消息进行封装
        String message = MessageUtil.getMessage(true, null, onlineUsers.keySet());
        //2.消息推送
        sendMessageToAll(message);
        //验证用户是否冻结
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        if(!name.equals("")){
            queryWrapper.eq("user_id", name);
            User user = userService.getOne(queryWrapper);
            if(!ObjectUtils.isEmpty(user) && user.getUserFreeze().getType()==1){
                String message1 = MessageUtil.getMessage(true, null,"FREEZE");
                sendToSomeOne(name, message1);
            }
        }

        //发送实时公告
        if(!realAnnounce.equals("")){
            String message1 = MessageUtil.getMessage(false, this.name, realAnnounce);
            sendMessageToAll(message1);
        }

    }


    /**
     * 发送信息给客户端
     * @param message 信息内容
     */
    private void sendMessageToAll(String message) {
        //将消息发送给所有的客户端
        //1.回去CurrenHashMap里面的key的Set集合（Set里面放的是用户名）
        // ConcurrentHashMap<String, WebSocketServer> onlineUsers; onlineUsers.put(name, this);
        Set<String> users=onlineUsers.keySet();
        //2.遍历key通过key获取ConcurrentHashMap里面保存的各个WebSocketServer对象(主要是获取里面的session)
        for (String user : users) {
            WebSocketServer webSocketServer = onlineUsers.get(user);
            try {
                //发送消息到客户端
                webSocketServer.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 连接关闭时调用
     * @ param session 当前会话
     * @ param closeReason 关闭原因
     */
    @OnClose
    public void OnClose() {
        onlineUsers.remove(this.name);
        String message = MessageUtil.getMessage(true, null, onlineUsers.keySet());
        sendMessageToAll(message);
        log.info(name+"：退出成功，当前连接人数为：{}", onlineUsers.size());
    }

    /**
     * 接收到客户端发送的数据时被调用
     * @param message 客户端发送过来的信息
     * @ param session 客户端会话
     */
    @OnMessage
    public void OnMessage(String message) {
        log.info("收到消息：{}", message);
        //根据用户名判断知道发送
        ObjectMapper mapper=new ObjectMapper();
        try {
            Message messageToOne = mapper.readValue(message, Message.class);
            switch (messageToOne.getMessage()) {
                case "getAllUsers": {
                    String message1 = MessageUtil.getMessage(true, null, onlineUsers.keySet());
                    sendToSomeOne(messageToOne.getToName(), message1);
                    break;
                }
                case "FREEZE": {
                    String message1 = MessageUtil.getMessage(true, null, messageToOne.getMessage());
                    sendToSomeOne(messageToOne.getToName(), message1);
                    break;
                }
                case "sendToAll": {
                    String message1 = MessageUtil.getMessage(true, null, onlineUsers.keySet());
                    sendMessageToAll(message1);
                    break;
                }
                case "clearReal": {
                   realAnnounce="";
                    break;
                }
                default: {
                    JSONObject parse = JSONObject.parseObject(messageToOne.getMessage());
                    if(parse.getString("type").equals("realAnnounce")){
                        realAnnounce=messageToOne.getMessage();
                    }
                    String message1 = MessageUtil.getMessage(false, this.name, messageToOne.getMessage());
                    sendMessageToAll(message1);
                    break;
                }
            }

        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送消息给指定的人
     * @param toName 发送的对象
     * @param message 消息内容
     */
    private void sendToSomeOne(String toName,String message){
        WebSocketServer webSocketServer = onlineUsers.get(toName);
        try {
            webSocketServer.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 连接错误是调用
     * @param session 当前会话
     * @param error 具体错误
     */
    @OnError
    public void OnError(Session session,Throwable error){
        error.printStackTrace();
    }

}